#pragma once
#include "MYSQL_api.hpp"
#include "Online_manger.hpp"

#define BOARD_ROW 15
#define BOARD_COL 15

#define CHESS_WHITE 1
#define CHESS_BLACK 2

#define CHECKLINENUM    \
    if (line_num >= 5)  \
        return user_id; \
    else                \
        line_num = 1;

enum room_status
{
    GAME_WAIT,
    GAME_START,
    GAME_OVER
};

class room
{
public:
    // 初始化房间类
    room(int room_id, Mysql_api *user_table, online_manger *online_user)
        : _room_id(room_id), _user_table(user_table), _online_user(online_user), _board(BOARD_ROW, vector<int>(BOARD_COL, 0))
    {
        _status = room_status::GAME_WAIT; // 房间初始状态为等待。
        _player_count = 2;
        DLOG("%d 号房间初始化成功", room_id);
    }
    ~room()
    {
        DLOG("%d 号房间已解散", _room_id);
    }
    // 返回房间id
    int get_room_id()
    {
        return _room_id;
    }

    // 放回房间状态
    room_status get_room_status()
    {
        return _status;
    }

    void updata_room_status(const room_status& status)
    {
        _status = status;
    }

    // 返回当前房间的游戏玩家
    int get_player_count()
    {
        return _player_count;
    }

    // 返回白棋用户id
    int get_white_user()
    {
        return _white_id;
    }
    int get_black_user()
    {
        return _black_id;
    }

    // 添加玩家用户id
    void add_white_id(int id)
    {
        _white_id = id;
    }
    void add_black_id(int id)
    {
        _black_id = id;
    }
    // 处理下棋动作
    Json::Value handle_chess(Json::Value &req) // req中需包含下棋的row, col, 以及玩家的id。
    {
        Json::Value temp;
        temp["optype"] = "put_chess";
        temp["result"] = false;
        temp["room_id"] = _room_id;

        // 2、当前下棋操作位置是否合理，监测对应的玩家是否退出游戏，若退出则当前玩家直接获胜
        // 监测对手是否退出游戏
        int user_id = req["uid"].asInt() == _white_id ? _white_id : _black_id; // 当前玩家的id
        int enemy_id = user_id == _white_id ? _black_id : _white_id;           // 对手的id
        if (!_online_user->in_room_hall(enemy_id))
        {
            // 对手掉线对用户进行分数的更新。
            _user_table->User_Win(user_id);
            _user_table->User_Lose(enemy_id);
            temp["result"] = true;
            temp["reason"] = "对方掉线，不战而胜";
            temp["winner"] = user_id;
            temp["row"] = req["row"].asInt();
             temp["col"] = req["col"].asInt();
            return temp;
        }
        // 判断下棋位置是否合理
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        if (_board[chess_row][chess_col])
        {
            temp["reason"] = "该位置已被占领";
            return temp;
        }

        // 3、监测下完棋之后该玩家是否直接取胜。
        int winner = check_board_winner(chess_row, chess_col, user_id);
        temp["result"] = true;
        if (winner)
        {
            temp["reason"] = "恭喜你，五星连珠战无敌";
            _user_table->User_Win(user_id);
            _user_table->User_Lose(enemy_id);
        }
        temp["winner"] = winner;
        temp["row"] = chess_row;
        temp["col"] = chess_col;
        _status = room_status::GAME_OVER;
        return temp;
    }

    // 处理玩家聊天的动作
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value text;
        text["optype"] = "chat";
        text["result"] = false;
        text["room_id"] = _room_id;
        text["uid"] = req["uid"];

        // 1、检查消息中是否包含敏感词
        std::string Str = req["message"].asString();
        size_t it = Str.find("垃圾");
        if (it != std::string::npos)
        {
            text["reason"] = "聊天信息包含敏感词";
            return text;
        }
        text["result"] = true;
        text["message"] = req["message"];
        // 广播函数
        return text;
    }

    // 处理玩家退出房间的动作
    void handle_exit(int uid)
    {
        // 1、下棋过程中退出房间，对手直接获胜/下棋结束后退出房间，则正常退出房间人数-1.
        if (_status == room_status::GAME_START)
        {
            Json::Value temp;
            temp["optype"] = "put_chess";
            //temp["result"] = false;
            temp["room_id"] = _room_id;
            _user_table->User_Win(uid == _white_id ? _black_id : _white_id);
            _user_table->User_Lose(uid);
            temp["result"] = true;
            temp["reason"] = "对方掉线，不战而胜";
            temp["winner"] = uid == _white_id ? _black_id : _white_id;
            temp["col"] = 999;
            //广播函数；
            broadcast(temp);
        }
        _status = room_status::GAME_OVER;
        _player_count--;
        return ;
    }

    //请求处理函数，根据不同请求类型调用不同的处理方法
    void handle_request(Json::Value &req)
    {
        Json::Value text;
        text["result"] = false;
        // 1、检测数据包中房间号与实际房间号是否一致
        int room_id = req["room_id"].asInt();
        if (room_id != _room_id)
        {
            text["reason"] = "房间号错误";
        }
        //2、根据不同的请求调用不同的方法。
        if(req["optype"].asString() == "put_chess")
        {
            text =  handle_chess(req);
        } else if(req["optype"].asString() == "chat")
        {
            text =  handle_chat(req);
        } 
        else {
            text["reason"] = "未知请求类型";
        }

        broadcast(text);
        return ;
    }

    //对执行完的信息进行广播
    void broadcast(Json::Value& req)
    {
        string Str;
        //1、对响应的数据进行序列化
        Json_api::serialize(req, Str);
        //2、获取房间中所有用户的通信连接
        wsserver_t::connection_ptr wconn, bconn;
        if(!_online_user->get_conn_from_room_id(_white_id, wconn))
        {
            ELOG("%d 用户连接不在房间中", _white_id);
        }
        else wconn->send(Str);
        if(!_online_user->get_conn_from_room_id(_black_id, bconn))
        {
            ELOG("%d 用户连接不在房间中", _black_id);
        } else bconn->send(Str);
        //3、发送响应的数据
        return ;
    }
private:
    // 检查是否决出胜者，返回0表示目前未分出胜负
    int check_board_winner(int row, int col, int user_id)
    {
        int num = user_id == _white_id ? 1 : 2;
        _board[row][col] = num;
        int line_num = 1;

        // 判断一条直线上的
        int tmp_row = row - 1, tmp_col = col;
        while (tmp_row > 0 && _board[tmp_row--][tmp_col] == num)
            line_num++;
        tmp_row = row + 1;
        while (tmp_row < BOARD_ROW && _board[tmp_row++][tmp_col] == num)
            line_num++;
        CHECKLINENUM

        // 判断一条横线上
        tmp_row = row;
        tmp_col = col - 1;
        while (tmp_col > 0 && _board[tmp_row][tmp_col--] == num)
            line_num++;
        tmp_col = col + 1;
        while (tmp_col < BOARD_COL && _board[tmp_row][tmp_col++] == num)
            line_num++;
        CHECKLINENUM

        // 判断是否在右线上。
        tmp_row = row - 1;
        tmp_col = col + 1;
        while (tmp_row > 0 && tmp_col < BOARD_COL && _board[tmp_row--][tmp_col++] == num)
            line_num++;
        tmp_row = row + 1;
        tmp_col = col - 1;
        while (tmp_row < BOARD_ROW && tmp_col > 0 && _board[tmp_row++][tmp_col--] == num)
            line_num++;
        CHECKLINENUM

        // 判断是否左斜线上
        tmp_row = row - 1;
        tmp_col = col - 1;
        while (tmp_row > 0 && tmp_col > 0 && _board[tmp_row--][tmp_col--] == num)
            line_num++;
        tmp_row = row + 1;
        tmp_col = col - 1;
        while (tmp_row < BOARD_ROW && tmp_col < BOARD_COL && _board[tmp_row++][tmp_col++] == num)
            line_num++;
        CHECKLINENUM

        return 0;
    }

private:
    int _room_id;                    // 房间id号
    room_status _status;             // 房间此时的状态 游戏等待、游戏中、游戏结束。
    int _player_count;               // 当前房间游戏玩家个数。
    int _white_id;                   // 白棋玩家的id
    int _black_id;                   // 黑棋用户的id
    Mysql_api *_user_table;          // 游戏结束后直接对用户数据进行更改。
    online_manger *_online_user;     // 对玩家进行在线状态进行管理
    std::vector<vector<int>> _board; // 棋盘
};

using room_ptr = std::shared_ptr<room>;

//房间管理类
class room_manager
{
public:
    room_manager(Mysql_api* user_table, online_manger* online_user)
    :_user_table(user_table), _online_user(online_user), _next_rid(1)
    {
        DLOG("房间管理模块初始化成功");
    }

    ~room_manager()
    {
        DLOG("房间管理模块初始化失败");
    }
    //为两个用户创建房间，并且返回对应的指针管理对象
    room_ptr create_room(int uid1, int uid2)
    {
         //为两个匹配到一起的用户创建
        //1、检验两个用户是否都在游戏大厅中；
        unique_lock<mutex> lock(_mutex);//保障在多线程情况下，数据的原子性，防止重入现象的出现。
        if((_online_user->in_game_hall(uid1) && _online_user->in_game_hall(uid2)) == 0)
        {
            DLOG("房间创建失败，有用户退出游戏");
            return room_ptr();
        }
        //2、创建好房间之后再将用户添加进来。
        shared_ptr<room> _room(new room(_next_rid, _user_table, _online_user));
        _room->add_black_id(uid1); _room->add_white_id(uid2);
        _room->updata_room_status(room_status::GAME_START);
        //3、将房间信息管理起来。
        _rooms.insert(make_pair(_next_rid, _room));
        _users.insert(make_pair(uid1, _next_rid));
        _users.insert(make_pair(uid2, _next_rid));
        DLOG("插入用户%d 和 %d 到管理中, 房间号为： %d",uid1, uid2, _next_rid);
        _next_rid++;
        //4、返回房间信息。
        return _room;
    }

    //根据房间id获取房间信息
    room_ptr get_room_from_rid(int rid)
    {
        std::unique_lock<mutex> lock(_mutex);
        unordered_map<int, room_ptr>::iterator rit = _rooms.find(rid);
        if(rit != _rooms.end())
        {
            return rit->second;
        }
        return room_ptr();
    }

    //根据用户id获取房间信息
    room_ptr get_room_from_uid(int uid)
    {
        std::unique_lock<mutex> lock(_mutex);
        //
        cout << "遍历房间信息:" << endl;
        for(auto e : _users)
        {
            cout << e.first <<" 存在房间管理中 " << e.second <<  endl;
        }
        auto rid = _users.find(uid);
        if(rid == _users.end())
        {
            DLOG("%d 用户找不到对应的房间连接",uid);
            return room_ptr();
        }
        //此处不能直接调用get_room_from_rid因为都包含互斥锁，直接调用会产生死锁效应。
        auto rit = _rooms.find(rid->second);
        if(rit != _rooms.end())
        {
            
            return rit->second;
        }
        return room_ptr();
    }

    //通过房间id销毁房间
    void remove_room(int rid)
    {
        //因为房间类room使用的是shared_ptr进行管理，所以只需要将其从_rooms中移除，使得其内部计数器count为0后shared_ptr就会自行释放。
        auto rit = _rooms.find(rid);
        if(rit == _rooms.end())
        {
            DLOG("销毁一个不存在的房间 %d", rid);
        }
        std::unique_lock<mutex> lock(_mutex);
        //先移除房间中用户。
        int uid1 = rit->second->get_white_user();
        int uid2 = rit->second->get_black_user();
        _users.erase(uid1);
        _users.erase(uid2);
        //
        DLOG("删除掉 %d 房间中的 %d 和 %d", rid, uid1, uid2);
        //销毁房间
        DLOG("%d 房间已被销毁", rid);
        _rooms.erase(rid);
        return ;
    }

    //删除指定玩家
    void remove_user(int uid)
    {
        room_ptr rip = get_room_from_uid(uid);
        if(rip == nullptr) return ;
        //处理玩家退出的动作
        rip->handle_exit(uid);
        _users.erase(uid);
        DLOG("%d 用户已从房间 %d 中删除, 此时房间还剩下玩家 %d", uid, rip->get_room_id(), rip->get_player_count());
        //如果房间中没有玩家直接销毁。
        if(rip->get_player_count() == 0)
        {
            _rooms.erase(rip->get_room_id());
            DLOG("%d 房间已被销毁", rip->get_room_id());
        }
    return ;
    }
private:
Mysql_api* _user_table;
online_manger* _online_user;
int _next_rid;
std::mutex _mutex;
unordered_map<int, room_ptr> _rooms; //映射房间id与房间类指针之间的关系。
unordered_map<int, int> _users;     //映射用户id与房间id的关系 
};